package com.lz.comm.widget.fragment

import android.content.Context
import android.database.DataSetObserver
import android.os.Parcel
import android.os.Parcelable
import android.os.Parcelable.ClassLoaderCreator
import android.util.AttributeSet
import android.widget.FrameLayout
import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import kotlin.math.min

/**
 *  author: you : 2019/11/7
 */
class FragmentLayout : FrameLayout {
    /**
     * 观察者
     */
    private var mObserver: FragmentObserver? = null

    var mAdapter: Adapter? = null
        private set

    var mCurrentPosition = NO_POSITION
        private set

    /**
     * (Fragment)是否附着窗体上
     */
    private var hasAttachedToWindow = false

    /**
     * 内存不足时的意外回收保存的数据
     */
    private var mRestoredAdapterState: Parcelable? = null

    private var mRestoredPosition = NO_POSITION

    private var mRestoredClassLoader: ClassLoader? = null

    constructor(context: Context) : super(context) {
        initId()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        initId()
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initId()
    }

    private fun initId() {
        if (id == NO_ID) {
            id = ViewCompat.generateViewId()
        }
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        if (!hasAttachedToWindow) { //onCreate时就初始位置,此处针对此问题修改
            hasAttachedToWindow = true
            setCurrentItem(mCurrentPosition)
        }
    }

    override fun onSaveInstanceState(): Parcelable? {
        val superState = super.onSaveInstanceState()
        val ss = SavedState(superState).apply { position = mCurrentPosition }
        mAdapter?.also { ss.adapterState = it.saveState() }
        return ss
    }

    override fun onRestoreInstanceState(state: Parcelable?) {
        if (state !is SavedState) {
            super.onRestoreInstanceState(state)
            return
        }
        super.onRestoreInstanceState(state.superState)
        if (mAdapter != null) {
            mAdapter!!.restoreState(state.adapterState, state.loader)
            setCurrentItem(state.position)
        } else {
            mRestoredPosition = state.position
            mRestoredAdapterState = state.adapterState
            mRestoredClassLoader = state.loader
        }
    }

    /**
     * 设置显示当前位置的Fragment
     */
    fun setCurrentItem(position: Int) {
        mAdapter?.takeIf { position >= 0 && position < it.count }?.also {
            mCurrentPosition = position
            if (hasAttachedToWindow) it.instantFragment(this, position)
        }
    }

    /**
     * 设置适配器
     *
     * @param adapter
     */
    fun setAdapter(adapter: Adapter) {
        mAdapter?.apply {
            setFragmentObserver(null)
            clearFragments()
            mCurrentPosition = -1
        }
        mAdapter = adapter
        mAdapter?.also {
            if (mObserver == null) mObserver = FragmentObserver()
            it.setFragmentObserver(mObserver)
            if (mRestoredPosition >= 0) {
                it.restoreState(mRestoredAdapterState, mRestoredClassLoader)
                setCurrentItem(mRestoredPosition)
                mRestoredPosition = -1
                mRestoredAdapterState = null
                mRestoredClassLoader = null
            }
        }
    }

    internal fun dataSetChanged() {
        mAdapter?.also {
            val newAdapterCount: Int = it.count
            if (newAdapterCount <= 0) {
                mCurrentPosition = -1
            } else {
                val position = min(newAdapterCount - 1, mCurrentPosition)
                setCurrentItem(position)
            }
        }
    }

    companion object {
        const val NO_POSITION = -1
    }

    private inner class FragmentObserver : DataSetObserver() {
        override fun onChanged() = dataSetChanged()

        override fun onInvalidated() = dataSetChanged()
    }

    /**
     * 内存不足或意外回收时的状态保存
     */
    internal class SavedState : BaseSavedState {
        var position = 0
        var adapterState: Parcelable? = null
        var loader: ClassLoader? = null

        constructor(superState: Parcelable?) : super(superState)
        private constructor(`in`: Parcel, loader: ClassLoader?) : super(`in`) {
            var loader = loader
            if (loader == null) {
                loader = javaClass.classLoader
            }
            position = `in`.readInt()
            adapterState = `in`.readParcelable(loader)
            this.loader = loader
        }

        override fun writeToParcel(out: Parcel, flags: Int) {
            super.writeToParcel(out, flags)
            out.writeInt(position)
            out.writeParcelable(adapterState, flags)
        }

        companion object {
            @JvmField
            val CREATOR = object : ClassLoaderCreator<SavedState> {

                override fun createFromParcel(`in`: Parcel, loader: ClassLoader) = SavedState(`in`, loader)

                override fun createFromParcel(`in`: Parcel) = SavedState(`in`, null)

                override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
            }
        }
    }

    /**
     * 默认适配器,切换时只保存fragment对象引用(onAttach, onCreate, onDetaach, onDestory只调用一次),不保存状态
     * 内存不足或意外回收不保存(Fragment.SaveState)状态
     */
    abstract class Adapter(internal val mManager: FragmentManager) {
        /**
         * 当前显示的Fragment
         */
        internal var mCurrentFragment: Fragment? = null

        /**
         * 当前FragmentManager容器中所有的模块, 用于销毁或刷新时清除所有的容器
         */
        internal var mFragments = ArrayList<Fragment?>()

        /**
         * 当前Fragment位置
         */
        internal var mCurrentPosition = -1

        private var mLayoutObserver: DataSetObserver? = null

        fun notifyDataSetChanged() {
            clearFragments()
            synchronized(this) {
                mLayoutObserver?.onChanged()
            }
        }

        /**
         * 刷新适配器,移除旧的Fragments
         */
        internal open fun clearFragments() {
            mCurrentPosition = -1
            mCurrentFragment = null
            val ft = mManager.beginTransaction()
            val iterator = mFragments.iterator()
            while (iterator.hasNext()) {
                iterator.next()?.also { ft.remove(it) }
                iterator.remove()
            }
            if (!ft.isEmpty) {
                ft.commitNowAllowingStateLoss()
            }
        }

        internal fun setFragmentObserver(observer: DataSetObserver?) {
            synchronized(this) { mLayoutObserver = observer }
        }

        abstract val count: Int

        abstract fun getItem(position: Int): Fragment

        open fun instantFragment(fm: FragmentLayout, position: Int) {
            if (mCurrentPosition == position) return
            val ft = mManager.beginTransaction()
            val itemId = getItemId(position)
            val name = makeFragmentName(fm.id, itemId)
            var fragment = mManager.findFragmentByTag(name)
            if (fragment != null) {
                ft.attach(fragment)
            } else {
                fragment = getItem(position)
                while (mFragments.size <= position) {
                    mFragments.add(null)
                }
                mFragments[position] = fragment
                ft.add(fm.id, fragment, name)
            }
            fragment.setMenuVisibility(true)
            ft.setMaxLifecycle(fragment, Lifecycle.State.RESUMED)
            mCurrentFragment?.also {
                ft.detach(it)
                it.setMenuVisibility(false)
                ft.setMaxLifecycle(it, Lifecycle.State.STARTED)
            }
            mCurrentFragment = fragment
            mCurrentPosition = position
            ft.commitNowAllowingStateLoss()
        }

        open fun saveState(): Parcelable? = null

        open fun restoreState(state: Parcelable?, loader: ClassLoader?) = Unit

        open fun getItemId(position: Int): Long = position.toLong()

        protected fun makeFragmentName(viewId: Int, id: Long) = "FragmentAdapter:$viewId:$id"
    }
}